﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.IO;
using System.Diagnostics;
using System.Collections.ObjectModel;
using System.Xml;
using ProgNetComponents.Serialization.Design;

namespace ProgNetComponents.Wcf.AppUpdate
{
    /// <summary>
    /// BaseAppUpdater
    /// </summary>
    public class BaseAppUpdater
    {
        private Serialization.ProgNetObjectSerializer serializer = new Serialization.ProgNetObjectSerializer();

        /// <summary>
        /// Initializes a new instance of the <see cref="BaseAppUpdater"/> class.
        /// </summary>
        public BaseAppUpdater()
        {
            RunAfterUpdate = true;
        }
        #region events
        /// <summary>
        /// Occurs when [copy progress].
        /// </summary>
        public event ProgNetComponents.Tools.FileTools.FileCopyProgress CopyProgress;
        /// <summary>
        /// Occurs when [task finished].
        /// </summary>
        public event EventHandler TaskFinished;
        /// <summary>
        /// Occurs when [task error].
        /// </summary>
        public event EventHandler TaskError;
        /// <summary>
        /// Occurs when [task canceled].
        /// </summary>
        public event EventHandler TaskCanceled;
        #endregion

        #region Properties
        /// <summary>
        /// Gets or sets application name
        /// </summary>
        public string AppName
        {
            get;
            set;
        }
        /// <summary>
        /// Gets or sets the version.
        /// </summary>
        /// <value>
        /// The version.
        /// </value>
        public string Version
        {
            get;
            set;
        }
        /// <summary>
        /// Gets or sets the zip file.
        /// </summary>
        /// <value>
        /// The zip file.
        /// </value>
        public string ZipFile
        {
            get;
            set;
        }
        /// <summary>
        /// Gets or sets the name of the runable.
        /// </summary>
        /// <value>
        /// The name of the runable.
        /// </value>
        public string RunableName
        {
            get;
            set;
        }
        /// <summary>
        /// Gets or sets the start parameters.
        /// </summary>
        /// <value>
        /// The start parameters.
        /// </value>
        public string StartParameters
        {
            get;
            set;
        }
        /// <summary>
        /// Gets or sets the application directory.
        /// </summary>
        /// <value>
        /// The application directory.
        /// </value>
        public string AppDirectory
        {
            get;
            set;
        }
        /// <summary>
        /// Gets or sets a value indicating whether [run after update].
        /// </summary>
        /// <value>
        ///   <c>true</c> if [run after update]; otherwise, <c>false</c>.
        /// </value>
        public bool RunAfterUpdate
        {
            get;
            set;
        }
       
        /// <summary>
        /// Gets the exceptions.
        /// </summary>
        /// <value>
        /// The exceptions.
        /// </value>
        [SkipSerialization]
        public ReadOnlyCollection<Exception> Exceptions
        {
            get;
            private set;
        }
        /// <summary>
        /// Gets or sets a value indicating whether [is runing].
        /// </summary>
        /// <value>
        ///   <c>true</c> if [is runing]; otherwise, <c>false</c>.
        /// </value>
        [SkipSerialization]
        private bool IsRuning { get; set; }
        #endregion

        #region methods
        /// <summary>
        /// Updates this instance.
        /// </summary>
        public void Update()
        {
            if (!IsRuning)
            {
                IsRuning = true;
                Task t = Task.Factory.StartNew(UpdateMethod);
                t.ContinueWith(Finished,  TaskContinuationOptions.OnlyOnRanToCompletion);
                t.ContinueWith(Error, TaskContinuationOptions.OnlyOnFaulted);
                t.ContinueWith(Canceled, TaskContinuationOptions.OnlyOnCanceled);
            }
        }

        /// <summary>
        /// Updates the method.
        /// </summary>
        private void UpdateMethod()
        {
            string localFile = Path.GetTempFileName()+Path.GetExtension(ZipFile);
            ProgNetComponents.Tools.FileTools.CopyFile(ZipFile, localFile, OnProgress);
            string ExtractParameters = "-o\"{0}\" -y".FormatMe(AppDirectory);

            using (Process p = Process.Start(localFile, ExtractParameters))
            {
                p.WaitForExit();
            }
            try
            {
                Task.Factory.StartNew(() => { File.Delete(localFile); });
            }
            catch { }
        }
        /// <summary>
        /// Called when [progress].
        /// </summary>
        /// <param name="fileLength">Length of the file.</param>
        /// <param name="bytesCopied">The bytes copied.</param>
        private void OnProgress(long fileLength, long bytesCopied)
        {
            if (CopyProgress != null)
                CopyProgress(fileLength, bytesCopied);
        }
        /// <summary>
        /// Finisheds the specified t.
        /// </summary>
        /// <param name="t">The t.</param>
        protected void Finished(Task t)
        {
            if (RunAfterUpdate)
                Process.Start(Path.Combine(AppDirectory, RunableName), StartParameters);
            if (TaskFinished != null)
                TaskFinished(this, EventArgs.Empty);
        }
        /// <summary>
        /// Errors the specified t.
        /// </summary>
        /// <param name="t">The t.</param>
        protected void Error(Task t)
        {
            Exceptions = t.Exception.InnerExceptions;
            if (TaskError != null)
                TaskError(this, EventArgs.Empty);
        }
        /// <summary>
        /// Canceleds the specified t.
        /// </summary>
        /// <param name="t">The t.</param>
        protected void Canceled(Task t)
        {
            if (TaskCanceled != null)
                TaskCanceled(this, EventArgs.Empty);
        }

        public override string ToString()
        {
            return serializer.Serialize(this).ToFormatedString();
        }
        public void FromString(string value)
        {
            serializer.Deserialize(this, value);
        }
        #endregion
    }
}
